import re
from app.services.ocr.utils.utils import is_tf


def normalize(text):
    """
    统一格式化文本：去标点、空格、大小写、中文数字
    """
    if not text:
        return ""
    text = text.strip().lower()
    text = re.sub(r'[^a-zA-Z0-9\u4e00-\u9fa5]', '', text)  # 只保留字母数字汉字
    return text


def extract_after_type(text):
    """
       去除题型标签，返回题干主体
    """
    for qtype in ["判断题", "单选题", "多选题", "单选", "多选"]:
        if qtype in text:
            return text.split(qtype)[-1].lstrip("：:").strip()
    return text.strip()


def extract_question_rich(raw_text):
    """
    提取题干，去除前缀、编号和问号后内容，仅保留核心问题部分
    """
    if not raw_text or not isinstance(raw_text, str):
        return ""

    text = raw_text.strip()
    tf = is_tf(text)
    # 清理前缀和开头编号，例如 “16、”
    text = re.sub(r'^.*?(\d+、)', '', text)

    # Step 1：清除前缀杂项（如“返回网上考试”等）
    text = re.sub(r'^.*?(剩余\d{1,3}s)?\s*[（(]?[单选多选判断]题?[)）]?\s*\d{1,4}[、.．，:：）)]*\s*', '', text,
                  flags=re.IGNORECASE)
    # Step 2：去掉尾部 Y/N 判断项（如 Y：正确 N:错误）
    text = re.sub(r'[Yy：:]\s*[正误确错][确误]?\s*[Nn：:]\s*[正误确错][确错]?', '', text)
    # Step 2: 清除其它题型前缀
    text = re.sub(r'^[^\u4e00-\u9fa5]{0,10}[\u4e00-\u9fa5]{0,4}题[)）]?\s*\d{1,3}[、.．:：]?', '', text)
    # Step 3: 清除纯数字题号
    text = re.sub(r'^\d{1,4}[、.，)]{1,2}', '', text)

    # ✅ Step 4: 按最后一个问号截断，否则按句号
    qm_matches = list(re.finditer(r'[？?]', text))
    if qm_matches:
        last_qm = qm_matches[-1]
        text = text[:last_qm.start() + 1]
    else:
        dot_matches = list(re.finditer(r'[。]', text))
        if dot_matches:
            last_dot = dot_matches[-1]
            text = text[:last_dot.start() + 1]
    # Step 5: 清除常见无用内容
    text = re.sub(r'(交管12123|学法减分|交通安全|驾驶员|请选择正确的答案|下一题|剩余\d+s?)', '', text)
    text = re.sub(r'[A-Za-z]?\d{5,}[A-Za-z]?', '', text)
    text = re.sub(r'[A-Za-z]{1,3}[:：]?\d{2,}', '', text)
    # 去除判断后面都不要
    # 去除末尾的 Y:正确 / N:错误 及其后续干扰项
    text = re.sub(r'(Y|N)[:：]?(正确|错误)[^\n]*$', '', text, flags=re.IGNORECASE)

    # Step 6: 压缩空格
    text = re.sub(r'\s+', ' ', text).strip()

    # Step 7: 判断题追加 N:错误Y:正确
    if tf:
        text += " N:错误Y:正确"

    return text


def extract_question(raw_text):
    """
    提取题干（从原始 OCR 文本中剔除选项部分，只保留题干）
    对于判断题，会统一在末尾追加 "N:错误Y:正确"
    """
    if not raw_text or not isinstance(raw_text, str):
        return ""

    text = raw_text.strip()

    text = re.sub(r'^.*?(?:剩余\d{1,3}S)?[（(][单多判断]选题[)）]?\d{0,3}[、.，)]?', '', text)

    # Step 2: 去掉题号（如 1、2.、3) 等）
    text = re.sub(r'^\d{1,4}[、.，)]{1,2}', '', text)

    # Step 1: 去掉题目前缀乱码 + 编号（如 剩余46S（判断题）11、）
    text = re.sub(r'^.*?[^\u4e00-\u9fa5]{0,5}[\d]{1,4}[、.，)]{1,2}', '', text)

    # Step 2: 移除 OCR 中已有的判断项，例如：
    text = re.sub(r'(N[:：]?[正确错误]{2}|Y[:：]?[正确错误]{2})+', '', text)

    # Step 4: 清理无关内容
    text = re.sub(r'(交管12123|学法减分|交通安全|驾驶员|请选择正确的答案|下一题|剩余\d+s?)', '', text)
    text = re.sub(r'[A-Za-z]?\d{5,}[A-Za-z]?', '', text)
    # Step 5: 去除无关字符（比如微信昵称的数字/英文乱入）
    text = re.sub(r'[A-Za-z]{1,3}[:：]?\d{2,}', '', text)
    text = re.sub(r'[A-D][:：]', '', text)
    # Step 6: 压缩空格
    text = re.sub(r'\s+', ' ', text).strip()
    text = extract_after_type(text)
    return text


def split_question_and_options(text):
    """
    分离题干与选项文本部分
    返回：题干, 选项部分
    """

    match = re.search(r'[A-D][\s\.、:：]', text)
    if not match:
        return text, ''  # 没有匹配上选项，说明整段可能是题干

    index = match.start()
    return text[:index], text[index:]


def extract_ocr_options(text):
    """
    更鲁棒地从OCR原文中提取A/B/C/D选项内容。
    自动忽略题干部分，只处理选项内容。
    返回： (options_list, raw_pairs_list)
    """
    import re

    def _normalize(txt):
        return txt.strip(" ：:.\n")

    def _split_question_and_options(text):
        # 尝试先找 A
        match = re.search(r'A[:：\.、]?', text)
        if match:
            return text[:match.start()], text[match.start():]

        # 找不到 A，找 B 或 C 或 D
        match = re.search(r'[B-D](?=[^\sA-D:：\.、])', text)
        if match:
            return text[:match.start()], text[match.start():]

        return text, ''

    # 分离题干与选项部分
    question_text, option_text = _split_question_and_options(text)

    if not option_text:
        return [], []

    # 若A缺失，尝试用题干最后一句话作为A选项内容
    if not re.match(r'^A[:：\.、.]', option_text):
        tail = re.findall(r'[^，。？！；.\n]{4,}$', question_text)
        if tail:
            option_text = f"A:{tail[-1]}" + option_text

    # 统一冒号格式，去除重复冒号（如C:：）
    option_text = re.sub(r'([A-D])[:：.]{1,}', r'\1:', option_text)

    # 补充标签和分隔符之间没空格或符号的情况，如 B准备抢行 → B:准备抢行
    option_text = re.sub(r'([A-D])(?=[^\sA-D:：.\.、])', r'\1:', option_text)

    # 匹配选项部分，非贪婪
    pattern = r'([A-D]):.(.*?)(?=([A-D]:.)|$)'
    matches = re.findall(pattern, option_text)

    # 生成字典方便补全
    pair_dict = {}
    for label, content, _ in matches:
        pair_dict[label] = _normalize(content)

    # 保证返回的选项列表完整，有缺失用空字符串填充
    labels = ['A', 'B', 'C', 'D']
    options = []
    raw_pairs = []
    for label in labels:
        val = pair_dict.get(label, '')
        options.append(val)
        raw_pairs.append((label, val))

    return options, raw_pairs


def prepare_query_text(raw_text):
    """
    从OCR文本中提取题干和选项，
    对选项排序后拼接返回
    """
    # 提取题干
    subject = extract_question_rich(raw_text)
    # 提取选项
    _, ocr_options = extract_ocr_options(raw_text)
    options = [opt[1].strip() for opt in ocr_options if opt[1].strip()]
    options.sort()
    # 拼接题干 + 排序后的选项
    full_text = subject + ' ' + ' '.join(options)
    return full_text


def match_options_by_library(ocr_text: str, lib_options: list):
    """
    从 OCR 原文中匹配题库标准答案，并恢复出完整的 A/B/C/D 结构。
    ocr_text: OCR识别到的原始选项文本
    lib_options: 标准题库中的选项内容，如：
        [
            '应该向从业单位所在地车辆管理所备案',
            '应在信息变更后三十日内向车管所备案',
            '应当在信息变更九十日内向车管所备案',
            '应该向驾驶证核发地车辆管理所备案'
        ]
    """

    import re

    # 去除 OCR 文本中的 A/B/C/D 等标记
    raw_text = re.sub(r'\b[A-D][\.、:：]?', '', ocr_text)

    matched = []
    remain_text = raw_text

    for opt in lib_options:
        index = remain_text.find(opt)
        if index != -1:
            matched.append((index, opt))
            remain_text = remain_text.replace(opt, '◎' * len(opt))  # 标记为已匹配
        else:
            matched.append((float('inf'), opt))  # 未匹配的排最后

    # 按顺序排序匹配的
    matched = sorted(matched, key=lambda x: x[0])
    ordered_opts = [item[1] for item in matched if item[0] != float('inf')]

    # 补齐为 4 项（OCR 可能少了）
    while len(ordered_opts) < 4:
        ordered_opts.append("")

    # 返回带标签的选项
    labeled = list(zip(['A', 'B', 'C', 'D'], ordered_opts))

    return labeled
